# a simple mips assembler
import sys
import os
import re
import binascii

assert(len(sys.argv)==3);


current_dir=os.path.join(os.path.abspath('.'),'samples');

asm_file=open(os.path.join(current_dir,sys.argv[1]),'r');

v_file=open(os.path.join(current_dir,sys.argv[2]),'w+');

# label system is supported

op_code={
    'ADD':'000000',
    'SUB':'000000',
    'AND':'000000',
    'OR':'000000',
    'SLT':'000000',
    'ADDI':'001000',
    'ANDI':'001100',
    'SLTI':'001010',
    'ORI':'001101',
    'J':'000010',
    'LW':'100011',
    'SW':'101011'
}

hex_dict={
    '0':'0000',
    '1':'0001',
    '2':'0010',
    '3':'0011',
    '4':'0100',
    '5':'0101',
    '6':'0110',
    '7':'0111',
    '8':'1000',
    '9':'1001',
    'A':'1010',
    'B':'1011',
    'C':'1100',
    'D':'1101',
    'E':'1110',
    'F':'1111'
}

bin_dict={
    '0000':'0',
    '0001':'1',
    '0010':'2',
    '0011':'3',
    '0100':'4',
    '0101':'5',
    '0110':'6',
    '0111':'7',
    '1000':'8',
    '1001':'9',
    '1010':'A',
    '1011':'B',
    '1100':'C',
    '1101':'D',
    '1110':'E',
    '1111':'F'
}


func_code={
    'ADD':'100000',
    'SUB':'100010',
    'SLT':'101010',
    'AND':'100100',
    'OR':'100101'
}

def hex2bin(hex_str):
    bin_str='';
    for c in hex_str:
        if(c=='_'): continue;
        bin_str+=hex_dict[c.upper()];
    return bin_str;

def mod(a,b):
    return a-(a//b)*b;

# should fill to five bits
def dec2bin(dec_str):
    return bin(int(dec_str))[2:].zfill(5);


def bin2hex(bin_str):
    assert(mod(len(bin_str),4)==0);
    hex_str='';
    for i in range(len(bin_str)//4):
        hex_str+=bin_dict[bin_str[4*i:4*i+4]];
    return hex_str;


def translate_line(instr):
    # insensitive to malicious syntax error
    NOP=re.compile("\s*nop\s+",re.IGNORECASE);
    SPECIAL=re.compile("\s*(add|sub|slt|and|or)\s+",re.IGNORECASE);
    I_FORMAT=re.compile("\s*(addi|slti|andi|ori)\s+",re.IGNORECASE);
    MEM_FORMAT=re.compile("\s*(lw|sw)\s+",re.IGNORECASE);
    J_FORMAT=re.compile("\s*j\s+",re.IGNORECASE);

    REG=re.compile("\s*r(\d+)\s*");
    IMM=re.compile("\$([\d|a-f|A-F]+)");
    COMMENT=re.compile("([\w|$|\s|,|(|)]+)\s*;\s*#[\w|\s|\W]*");


    # first filter out the comment
    m=COMMENT.match(instr);


    if(m!=None):
        instr=m.group(1);



    code='';

    if(NOP.match(instr)!=None):
        return hex2bin('0000_0000');

    # fall into alu operations
    m=SPECIAL.match(instr);
    if(m!=None):
        regs=REG.findall(instr);
        assert(len(regs)==3);
        code+=op_code[m.group().strip().upper()];
        for reg_i in regs:
            code+=dec2bin(reg_i);
        code+='00000'; # sa
        code+=func_code[m.group().strip().upper()];
        return code;

    m=I_FORMAT.match(instr);
    if(m!=None):
        regs=REG.findall(instr);
        imm=IMM.findall(instr);
        assert(len(regs)==2);
        assert(len(imm)==1);
        code+=op_code[m.group().strip().upper()];
        for reg_i in regs:
            code+=dec2bin(reg_i);
        code+=hex2bin(imm[0]);
        return code;


    m=MEM_FORMAT.match(instr);
    if(m!=None):
        imm=IMM.findall(instr);
        regs=REG.findall(instr);
        assert(len(regs)==2);
        assert(len(imm)==1);
        code+=op_code[m.group().strip().upper()];
        # be careful of the order
        code+=dec2bin(regs[1]);
        code+=dec2bin(regs[0]);
        code+=hex2bin(imm[0]);
        return code;

    m=J_FORMAT.match(instr);
    if(m!=None):
        imm=IMM.findall(instr);
        assert(len(imm)==1);
        code+=op_code[m.group().strip().upper()];
        code+=hex2bin(imm[0]).zfill(26);
        return code;


    print("%s Syntax Error or Undefined"%(instr));




count=0;
for instr in asm_file:
    if(instr.strip()==''):
        continue;
    transformed=translate_line(instr);
    # output the hex as well
    v_file.write("storage[%d]<=32'b%s; // HEX: %s\n"%(count,transformed, bin2hex(transformed)));
    count+=1;







asm_file.close();
v_file.close();
